package export;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExporter;
import net.sf.jasperreports.engine.JRExporterParameter;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;

import org.apache.commons.io.output.ByteArrayOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ar.com.fdvs.dj.core.DynamicJasperHelper;
import ar.com.fdvs.dj.core.layout.ClassicLayoutManager;
import ar.com.fdvs.dj.domain.DynamicReport;
import ar.com.fdvs.dj.domain.builders.DynamicReportBuilder;


/**
 * Ancestor class of all Exporter in application. For using it, calling <code>configExporter</code> method before
 * calling <code>export</code>.
 * 
 * 
 */
public abstract class AbstractExporter {

    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractExporter.class);

    protected JRExporter exporter;

    /**
     * In constructor template method initialize will be invoked to create and set default parameter for export.
     */
    public AbstractExporter() {
        initialize();
    }

    /**
     * Initializing exporter. Sub class override this method to initialize their exporter.
     */
    protected abstract void initialize();

    /**
     * Exporter will be set default parameter before export to file.
     */
    public abstract void configExporter(ExportRequest request);

    /**
     * Export to file. * @param builders List of builder to suppose multi-report in one file.
     * 
     * @param dataSources
     *            List of dataSource used for report.
     * @param request
     *            Export request.
     * @throws JRException
     *             Jasper report exception.
     * @throws IOException
     *             Exception will throw if having problem when working with file.
     */
    public InputStream export(List<DynamicReportBuilder> builders, List<JRDataSource> dataSources, 
            ExportRequest request)
            throws JRException, IOException {
        // create Jasper printer list which will use to export.
        List<JasperPrint> listPrinter = new ArrayList<JasperPrint>();

        int width = 0;

        // generate Jasper print according to builder and data source.
        for (int i = 0; i < builders.size(); i++) {
            // reset value
            boolean isReportBuilder = false;
            width = 0;

            if (builders.get(i) instanceof ReportBuilder && ((ReportBuilder) builders.get(i)).isAutoWidth()) {
                isReportBuilder = true;
            }

            // get true page width for needed information
            DynamicReport dynamicreport = builders.get(i).setMargins(0, 0, 0, 0).build();
            if (isReportBuilder) {
                width = ((ReportBuilder) builders.get(i)).getPageWidth();
            }

            // compile to Jasper report object
            JasperReport report = DynamicJasperHelper.generateJasperReport(dynamicreport, new ClassicLayoutManager(),
                    request.getParameters());

            if (isReportBuilder) {
                width = width + report.getLeftMargin() + report.getRightMargin();
            }

            AbstractCustomReportCommand command = request.getAdditionalProperty(
                    ReportPropertyConstants.CUSTOM_REPORT_COMMAND, AbstractCustomReportCommand.class);

            // execute report if it is not empty
            if (command != null) {
                command.execute(report, width);
            }

            // check custom report command property
            JasperPrint printer = JasperFillManager.fillReport(report, request.getParameters(), dataSources.get(i));

            // set new page size if it match the condition: is ReportBuilder instance and activated autoWidth mode.
            if (isReportBuilder) {
                printer.setPageWidth(width);
            }
            listPrinter.add(printer);
        }

        ByteArrayOutputStream fos = new ByteArrayOutputStream();
        exporter.setParameter(JRExporterParameter.JASPER_PRINT_LIST, listPrinter);
        exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, fos);

        exporter.exportReport();
        fos.close();

        return new ByteArrayInputStream(fos.toByteArray());
    }

    /**
     * Open/create the file.
     * 
     * @param path
     *            the path
     * 
     * @return the open output stream
     * 
     * @throws JRException
     *             Jasper report exception.
     * @throws IOException
     *             Having problem when working with file.
     */
    protected FileOutputStream openFile(String path) throws JRException, IOException {

        File outputFile = new File(path);
        File parentFile = outputFile.getParentFile();
        if (parentFile != null) {
            parentFile.mkdirs();
        }
        return new FileOutputStream(outputFile);
    }
}
